package terra.model.object;

import java.util.ArrayList;
import java.util.HashMap;

import terra.Config;
import terra.definitions.NpcDef;
import terra.managers.DefManager;
import terra.managers.MapManager;
import terra.managers.ThreadPoolManager;
import terra.model.object.map.AbstractMapObject;
import terra.model.object.map.GroundObject;
import terra.model.object.map.MapObject;
import terra.network.packet.server.StaticInfo;
import terra.util.MathUtil;

public class Tree extends StaticObject {
	private static final long serialVersionUID = 1L;
	/*
	 * Класс дерева Состоит из связанных между собой блоков. Каждый выше стоящий
	 * блок зависит от более нижнего Ствол можно исопльзовать как лестницу,
	 * листва отрастает заново.
	 */
	HashMap<Integer, GroundObject> tree = new HashMap<Integer, GroundObject>();
	ArrayList<GroundObject> leaves = new ArrayList<GroundObject>();
	public String leavesImage;
	public int treeHeight = 15;
	public int leavesCount = 10;
	private int hp, maxHp;
	private int itemId, leavesItemId;
	public int groundId, leavesGroundId;

	public Tree(){
		super();
	}	
	public Tree(int objectId,int defId){
		super(objectId,defId);
	}
	
	public void applyDef(NpcDef d) {
		setMaxHp(d.getIntProperty("baseHp"));
		setName(d.getStringProperty("name"));
		setImage(d.getStringProperty("image"));
		leavesImage = d.getStringProperty("leavesImage");
		treeHeight = d.getIntProperty("minHeight")
				+ MathUtil.rnd.nextInt(d.getIntProperty("maxHeight")
						- d.getIntProperty("minHeight"));
		leavesCount = d.getIntProperty("minLeaves")
				+ MathUtil.rnd.nextInt(d.getIntProperty("maxLeaves")
						- d.getIntProperty("minLeaves"));
		itemId = d.getIntProperty("itemId");
		leavesItemId = d.getIntProperty("leavesItemId");
		groundId = d.getIntProperty("groundId");
		leavesGroundId = d.getIntProperty("leavesGroundId");
	}

	/**
	 * создаем дерево, начиная с нижнего блока
	 * 
	 * @throws Exception
	 */
	public void makeTree(AbstractMapObject map) {
		if (map == null) {
			return;
		}
		((GroundObject) map).setMaxHp(((GroundObject) map).getMaxHp()
				+ getMaxHp());
		AbstractMapObject back = map.getNeighbor(MapObject.UP);
		int count = getMaxHp() / treeHeight;
		for (int i = 0; i < treeHeight; i++) {
			if (back == null)
				back = new MapObject(map.getX(), map.getY() - i
						* Config.tileSize, map.getTileX(),
						(short) (map.getTileY() - i),
						DefManager.groundDefs.get(-1), map.getMap());
			GroundObject treeBlock = new GroundObject(back.getX(), back.getY(),
					back.getTileX(), back.getTileY(),
					DefManager.groundDefs.get(groundId), back.getMap());
			back.setMass(0);
			treeBlock.setBackgroundId(back.getId());
			if (i != 0)
				treeBlock.setMaxHp(getMaxHp() - count * i);
			else
				treeBlock.setMaxHp(getMaxHp() - count * i);
			MapManager.instance.getMaps().get(getMapIndex()).getMap()[treeBlock
					.getTileX()][treeBlock.getTileY()] = treeBlock;
			tree.put(i, treeBlock);
			treeBlock.addListener(this);
			back = treeBlock.getNeighbor(MapObject.UP);
		}
		setX(tree.get(tree.size() - 1).getX());
		setY(tree.get(tree.size() - 1).getY());
		setWidth(Config.tileSize);
		setHeight(Config.tileSize * treeHeight);

		for (int i = 0; i < leavesCount; i++) {
			int rndHeight = MathUtil.rnd.nextInt(treeHeight - 4) + 4;
			boolean left = MathUtil.rnd.nextBoolean();
			AbstractMapObject air = tree.get(rndHeight).getNeighbor(
					left ? MapObject.LEFT : MapObject.RIGHT);
			// FIXME!!!
			if (air == null)
				air = new MapObject(map.getX()
						+ (left ? -Config.tileSize : Config.tileSize), tree
						.get(rndHeight).getY(), (short) (tree.get(rndHeight)
						.getTileX() + (left ? -1 : 1)), tree.get(rndHeight)
						.getTileY(), DefManager.groundDefs.get(-1),
						map.getMap());
			if (air.getImage() == leavesImage)
				air = air.getNeighbor(MathUtil.rnd.nextBoolean() ? MapObject.UP
						: MapObject.DOWN);
			if (air.getImage() == getImage())
				continue;
			for (GroundObject leaf : leaves)
				if (leaf.getTileY() == rndHeight) {
					i--;
					continue;
				}
			GroundObject leavesBlock = new GroundObject(air.getX(), air.getY(),
					air.getTileX(), air.getTileY(),
					DefManager.groundDefs.get(leavesGroundId), air.getMap());
			leavesBlock.setBackgroundId((short) -1);
			leaves.add(leavesBlock);
			MapManager.instance.getMaps().get(getMapIndex()).getMap()[leavesBlock
					.getTileX()][leavesBlock.getTileY()] = leavesBlock;
			leavesBlock.addListener(this);
		}
	}

	@Override
	public void update(int delta) {
		if (isRemove())
			return;
		super.update(delta);

		if ((getTile().getNeighbor(MapObject.UP).getType() == 2 && getTile()
				.getNeighbor(MapObject.UP).getMass() >= 0.5f)) {
			removeObject();
		}
	}

	@Override
	protected void removeObject() {
		if (isRemove())
			return;
		setRemove(true);
		super.removeObject();
		for (GroundObject obj : tree.values()) {
			obj.removeListener(this);
			ThreadPoolManager.getInstance().scheduleGeneral(
					new treeBlockRemover(obj, false),
					250 + MathUtil.rnd.nextInt(500));
		}
		tree.clear();
		for (GroundObject obj : leaves) {
			obj.removeListener(this);
			ThreadPoolManager.getInstance().scheduleGeneral(
					new treeBlockRemover(obj, true),
					1000 + MathUtil.rnd.nextInt(1250));
		}
		leaves.clear();
		System.out.println("remove tree height " + treeHeight + " ; blocks "
				+ tree.size() + " ;  leafs " + leaves.size());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * terra.model.object.StaticObject#objectChanged(terra.model.object.GObject)
	 * Наносим удар только текущему блоку, но когда его хп кончается - ломаются
	 * все более верхние блоки + листва на уровне этого блока и выше начинает
	 * опадать
	 */
	public void objectChanged(GObject object) {

		if (leaves.contains(object)) {
			leaves.remove(object);
			return;
		}
		if (!(object instanceof GroundObject))
			return;
		GroundObject obj = (GroundObject) object;
		if (obj.getHp() <= 0) {
			removeTreeBlock(obj);
			if (obj.getNeighbor(MapObject.UP) != null
					&& !(obj.getNeighbor(MapObject.UP) instanceof MapObject)) {
				GroundObject neighbor = (GroundObject) obj
						.getNeighbor(MapObject.UP);
				while (neighbor != null && tree.containsValue(neighbor)) {
					neighbor.hit(neighbor.getHp());
					removeTreeBlock(neighbor);
					if (neighbor.getNeighbor(MapObject.UP) != null
							&& !(obj.getNeighbor(MapObject.UP) instanceof MapObject))
						neighbor = (GroundObject) neighbor
								.getNeighbor(MapObject.UP);
					else
						break;
				}
			}
			checkForDeadLeaves(obj);
		}

		// int n = 0;
		// for(int i = 0; i < tree.size(); i++) {
		// if (tree.get(i) == object) {
		// changed = (GroundObject) object;
		// n = i;
		// break;
		// }
		// }
		// System.out.println("Tree changed object! "+changed);
		// if (changed == null)
		// return;
		// int count = getMaxHp()/treeHeight;
		// int maxHp = getMaxHp() - count*n;
		// int diff = maxHp-changed.getHp();
		// changed.setHp(changed.getMaxHp()-diff);
		// if (changed.getHp()<= 0) {
		// for(int j = n; j < treeHeight;j++) {
		// tree.get(j).setHp(0);
		// System.out.println("Tree setHp(0);! "+object);
		//
		// ThreadPoolManager.getInstance().scheduleGeneral(new
		// treeBlockRemover(tree.get(j),false), 250+ Util.rnd.nextInt(250));
		// }
		//
		// for(int i = 0; i < n;i++){
		// tree.get(i).setHp(tree.get(i).getHp()-changed.getMaxHp());
		// System.out.println("Tree setHp(tree.get(i).getHp()-changed.getMaxHp());! "+(tree.get(i).getHp()-changed.getMaxHp()));
		// }
		//
		// treeHeight = treeHeight - (treeHeight-n);
		// checkForDeadLeaves(changed);
		// }
	}

	void removeTreeBlock(GroundObject obj) {
		for (int i = 0; i < tree.size(); i++) {
			if (tree.get(i) == obj) {
				tree.remove(i);
				return;
			}
		}
	}

	private void checkForDeadLeaves(GroundObject changed) {
		int i = 0;
		for (int j = 0; j < leaves.size(); j++) {
			final GroundObject leave = leaves.get(j);
			leave.removeListener(this);
			if (leave.getTileY() <= changed.getTileY()) {
				// System.out.println("remove dead leaf");
				i++;
				leaves.remove(leave);
				ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {

					@Override
					public void run() {
						leave.hit(10000);

					}
				}, 1000 + MathUtil.rnd.nextInt(i * 1000));
			}
		}
	}

	//
	//
	// private void doItemDrop(GroundObject groundObject,boolean isLeaf) {
	// //System.out.println("tree itemdrop  isLeaf? " + isLeaf);
	// if (isLeaf) {
	// ItemObject item = new ItemObject();
	// item.setX(groundObject.getX());
	// item.setY(groundObject.getY()-Config.tileSize);
	// item.setWidth(Config.tileSize);
	// item.setHeight(Config.tileSize);
	// //item.setMapIndex(groundObject.getMap().x);
	// item.setGroundTileX(groundObject.getTileX());
	// item.setGroundTileY(groundObject.getTileY()-1);
	// item.item = new
	// Item(DefManager.allItems.get(groundObject.getDef().itemId),null);
	// item.setName(item.item.getName());
	// World.getInstance().addObject(item.getObjectId(), item);
	// }
	// else {
	// Util.doItemDrop(NpcManager.getInstance().npcDefs.get(getDef()).drop,
	// getTile());
	// }
	// }

	public int getMaxHp() {
		return maxHp;
	}

	public void setMaxHp(int maxHp) {
		this.maxHp = maxHp;
		hp = maxHp;
	}

	public int getHp() {
		return hp;
	}

	public void setHp(int hp) {
		this.hp = hp;
	}

	public int getItemId() {
		return itemId;
	}

	public void setItemId(int itemId) {
		this.itemId = itemId;
	}

	public int getLeavesItemId() {
		return leavesItemId;
	}

	public void setLeavesItemId(int leavesItemId) {
		this.leavesItemId = leavesItemId;
	}

	class treeBlockRemover implements Runnable {
		GroundObject block;
		boolean isLeaf;

		public treeBlockRemover(GroundObject block, boolean isLeaf) {
			this.block = block;
			this.isLeaf = isLeaf;
		}

		@Override
		public void run() {
			if (!isLeaf)
				tree.remove(block);
			else
				leaves.remove(block);
			block.hit(1000);
			// MapObject obj = new MapObject(block);
			// //obj.isCave = true;
			// MapManager.instance.getMaps().get(block.getMap().x).map[block.getTileX()][block.getTileY()]
			// = obj;
			// doItemDrop(block,isLeaf);
		}

	}

	class leavesGenerator implements Runnable {
		GroundObject oldLeaf;

		public leavesGenerator(GroundObject object) {
			oldLeaf = object;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub

		}

	}

	@Override
	public StaticInfo getInfo() {

		return null;
	}

	@Override
	public void applyDef() {
		// TODO Auto-generated method stub
		
	}
}
